/*******************************************************************************
 * Copyright (c) 2008-2009 zhang yuexiang.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ 
package org.xfeep.alaya.orm.jpa;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.metamodel.Metamodel;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;

import org.xfeep.alaya.jta.JtaTransactionListener;
import org.xfeep.alaya.jta.TransactionLifeCycleMonitorService;
import org.xfeep.asura.core.ComponentType;
import org.xfeep.asura.core.CoreConsts;
import org.xfeep.asura.core.annotation.Config;
import org.xfeep.asura.core.annotation.Ref;
import org.xfeep.asura.core.annotation.Service;

@Service(interfaces={EntityManager.class}, type=ComponentType.ON_DEMAND)
@Config("")
public class SmartEntityManagerImp implements EntityManager, JtaTransactionListener {

	@Ref(matcher="_cid=$._cid"+CoreConsts.ON_DEMAND_SEPRATOR)
	EntityManagerFactoryImp entityManagerFactory;
	
	@Ref
	TransactionManager transactionManager;
	
	Map<Transaction, EntityManager> transactionToDelegatesMap = new ConcurrentHashMap<Transaction, EntityManager>();


	public EntityManager getDelegate() {
		Transaction tx = null;
		try {
			tx = transactionManager.getTransaction();
		} catch (SystemException e) {
			throw new IllegalStateException("Cannot get current transaction", e);
		}
		if (tx == null){
			throw new IllegalStateException("SmartEntityManagerImp Service must be used in a container-managed transaction service");
		}
		EntityManager delegate = transactionToDelegatesMap.get(tx);
		if (delegate == null){
			transactionToDelegatesMap.put(tx, delegate = entityManagerFactory.createEntityManager());
			try {
				TransactionLifeCycleMonitorService.addSynchronization(tx, this);
			} catch (Exception e) {
					throw new IllegalStateException("can not register and join transaction", e);
			}
		}
		delegate.joinTransaction();
		return delegate;
	}
	

	public void close() {
		throw new IllegalStateException("can not close a contianer-managed EntityManager");
	}



	public void afterCompletion(Transaction tx , int status) {
		TransactionLifeCycleMonitorService.removeSynchronization(tx, this);
		EntityManager delegate = transactionToDelegatesMap.remove(tx);
		delegate.close();

	}

	public void beforeCompletion(Transaction tx ) {
	}



	public void clear() {
		getDelegate().clear();
	}


	public boolean contains(Object arg0) {
		return getDelegate().contains(arg0);
	}


	public <T> TypedQuery<T> createNamedQuery(String arg0, Class<T> arg1) {
		return getDelegate().createNamedQuery(arg0, arg1);
	}


	public Query createNamedQuery(String arg0) {
		return getDelegate().createNamedQuery(arg0);
	}


	public Query createNativeQuery(String arg0, Class arg1) {
		return getDelegate().createNativeQuery(arg0, arg1);
	}


	public Query createNativeQuery(String arg0, String arg1) {
		return getDelegate().createNativeQuery(arg0, arg1);
	}


	public Query createNativeQuery(String arg0) {
		return getDelegate().createNativeQuery(arg0);
	}


	public <T> TypedQuery<T> createQuery(CriteriaQuery<T> arg0) {
		return getDelegate().createQuery(arg0);
	}


	public <T> TypedQuery<T> createQuery(String arg0, Class<T> arg1) {
		return getDelegate().createQuery(arg0, arg1);
	}


	public Query createQuery(String arg0) {
		return getDelegate().createQuery(arg0);
	}


	public void detach(Object arg0) {
		getDelegate().detach(arg0);
	}


	public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2,
			Map<String, Object> arg3) {
		return getDelegate().find(arg0, arg1, arg2, arg3);
	}


	public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2) {
		return getDelegate().find(arg0, arg1, arg2);
	}


	public <T> T find(Class<T> arg0, Object arg1, Map<String, Object> arg2) {
		return getDelegate().find(arg0, arg1, arg2);
	}


	public <T> T find(Class<T> arg0, Object arg1) {
		return getDelegate().find(arg0, arg1);
	}


	public void flush() {
		getDelegate().flush();
	}


	public CriteriaBuilder getCriteriaBuilder() {
		return getDelegate().getCriteriaBuilder();
	}


	public EntityManagerFactory getEntityManagerFactory() {
		return getDelegate().getEntityManagerFactory();
	}


	public FlushModeType getFlushMode() {
		return getDelegate().getFlushMode();
	}


	public LockModeType getLockMode(Object arg0) {
		return getDelegate().getLockMode(arg0);
	}


	public Metamodel getMetamodel() {
		return getDelegate().getMetamodel();
	}


	public Map<String, Object> getProperties() {
		return getDelegate().getProperties();
	}


	public <T> T getReference(Class<T> arg0, Object arg1) {
		return getDelegate().getReference(arg0, arg1);
	}


	public EntityTransaction getTransaction() {
		return getDelegate().getTransaction();
	}


	public boolean isOpen() {
		return getDelegate().isOpen();
	}


	public void joinTransaction() {
		getDelegate().joinTransaction();
	}


	public void lock(Object arg0, LockModeType arg1, Map<String, Object> arg2) {
		getDelegate().lock(arg0, arg1, arg2);
	}


	public void lock(Object arg0, LockModeType arg1) {
		getDelegate().lock(arg0, arg1);
	}


	public <T> T merge(T arg0) {
		return getDelegate().merge(arg0);
	}


	public void persist(Object arg0) {
		getDelegate().persist(arg0);
	}


	public void refresh(Object arg0, LockModeType arg1, Map<String, Object> arg2) {
		getDelegate().refresh(arg0, arg1, arg2);
	}


	public void refresh(Object arg0, LockModeType arg1) {
		getDelegate().refresh(arg0, arg1);
	}


	public void refresh(Object arg0, Map<String, Object> arg1) {
		getDelegate().refresh(arg0, arg1);
	}


	public void refresh(Object arg0) {
		getDelegate().refresh(arg0);
	}


	public void remove(Object arg0) {
		getDelegate().remove(arg0);
	}


	public void setFlushMode(FlushModeType arg0) {
		getDelegate().setFlushMode(arg0);
	}


	public void setProperty(String arg0, Object arg1) {
		getDelegate().setProperty(arg0, arg1);
	}


	public <T> T unwrap(Class<T> arg0) {
		return getDelegate().unwrap(arg0);
	}
	

}
